package LIR;

import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;

import IC.AST.Field;
import IC.AST.ICClass;
import IC.AST.Method;
import IC.AST.StaticMethod;

public class ClassLayout 
{
	public Map<String, Integer> methodToOffset = new LinkedHashMap<String, Integer>();
    public Map<String, Integer> fieldToOffset = new LinkedHashMap<String, Integer>();
    public Map<Integer, Method> offsetToMethod = new HashMap<Integer, Method>();

    private ICClass m_IcClass;
    
    public ClassLayout(ICClass icClass, ClassLayout parentLayout)
    {
    	m_IcClass = icClass;
    	 
    	if (parentLayout != null)
    	{
    		// Copy parent methods
    		for (String method : parentLayout.methodToOffset.keySet())
    			methodToOffset.put(method, parentLayout.methodToOffset.get(method));
    		
    		for (Integer offset : parentLayout.offsetToMethod.keySet())
    			offsetToMethod.put(offset, parentLayout.offsetToMethod.get(offset));
    		
    		// Copy parent fields
    		for (String field : parentLayout.fieldToOffset.keySet())
    			fieldToOffset.put(field, parentLayout.fieldToOffset.get(field));
    	}

    	for (Method method : icClass.getMethods())
    	{
    		if (method instanceof StaticMethod)
    			continue;
    		
    		boolean foundMethod = false;
    		String methodName = method.getName();
    		
    		// Search for parent method to override
    		for (String existingMethod : methodToOffset.keySet())
    		{
    			if (method.getName().equals(existingMethod))
    			{
    				int offset = methodToOffset.get(existingMethod);
    				
    				// Replace with new method
    				methodToOffset.put(methodName, offset);
    				offsetToMethod.put(offset, method);
    				foundMethod = true;
    			}
    		}
    		
    		// If this is a new method, append it to our layout
    		if (!foundMethod)
    		{
    			int offset = methodToOffset.size();
    			
    			methodToOffset.put(methodName, offset);
    			offsetToMethod.put(offset, method);
    		}
    	}
    	
    	// Append fields
    	for (Field field: icClass.getFields())
    	{
    		fieldToOffset.put(field.getName(), fieldToOffset.size());
    	}
    }
    
    public String getDispatchTable()
    {
    	String translate = "_DV_" + m_IcClass.getName() + ": [";
    	
    	for (int i = 0; i < offsetToMethod.size(); i++)
    	{
    		Method method = offsetToMethod.get(i);
    		String methodName = "_" + method.getSymbolTable().getClassSymbolTable().getId() + "_" + method.getName();
    		translate += methodName;
    		
    		if (i != (offsetToMethod.size() - 1))
    			translate += ",";
    	}
    	
    	translate += "]\n";
    	
    	return translate;
    }
    
    public String getClassName()
    {
    	return m_IcClass.getName();
    }
    
    public int getMethodOffset(String className, String methodName)
    {
    	return methodToOffset.get(methodName);
    }
    
	public int getSize()
	{
		 return 4*(fieldToOffset.size() + 1);
	}
}
